﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;

namespace SarjetLib
{
  public class PathDecoderUri
  {
    public static Regex repRelSign = new Regex(@"\.\.\.[/\\]+", RegexOptions.Compiled);
    public static Regex repRelSign2 = new Regex(@"\.\.[/\\](?=\.)", RegexOptions.Compiled);
    //public string curpath;
    public Uri uriCurpath = null;
    Uri _uriAbs;
    public Uri uriAbs
    {
      get => _uriAbs;
      set
      {
        _uriAbs = value;
        split = _uriAbs.Segments.ToList();
        if (split?.First() == "/")
        {
          split.RemoveAt(0);
        }
        if (split?.Last().IndexOf('/') == -1)
        {
          fne = split.Last();
        }
      }
    }
    Uri uriRel = null;
    public bool isfile { get => split?.Count == 0 ? false : split?.Last().IndexOf('/') == -1; }
    List<string> split = null;

    public PathDecoderUri DirRemoveLastn(int lastn = 0)
    {
      lastn++;
      if (split?.Count > lastn && isfile)
      {
        split.RemoveAt(split.Count - 1 - lastn);
      }
      else
      {
        split.RemoveAt(split.Count - lastn);
      }
      TryRecombine();
      return this;
    }

    public PathDecoderUri DirAppend(string dirname)
    {
      dirname = dirname.Trim('/') + '/';
      if (split?.Count > 0 && isfile)
      {
        split.Insert(split.Count - 1, dirname);
      }
      else
      {
        split.Add(dirname);
      }
      TryRecombine();
      return this;
    }

    public void TryRecombine()
    {
      string fullpath = string.Join("", split);
      Uri tmpUriAbs;
      try
      {
        tmpUriAbs = new Uri(fullpath);
      }
      catch
      {
        return;
      }
      uriAbs = tmpUriAbs;
      AbsToRel(uriAbs.LocalPath);
    }

    public string curDir
    {
      get
      {
        if (split?.Last().IndexOf('/') != -1)
        {
          return split.Last().TrimEnd('/');
        }
        else
        {
          var skip1 = split?.Reverse<string>().Skip(1).First();
          if (skip1.IndexOf('/') != -1)
          {
            return skip1.TrimEnd('/');
          }
        }
        return "";
      }
    }

    public PathDecoderUri ReNameExt(string fnameext)
    {
      if (fnameext?.Length > 0)
      {
        fne = fnameext;
        TryRecombine();
      }
      return this;
    }

    public PathDecoderUri ReExt(string ext)
    {
      if (ext?.Length > 0)
      {
        fne = $"{fname}.{ext}";
        TryRecombine();
      }
      return this;
    }

    public string fne
    {
      get
      {
        return string.IsNullOrEmpty(_ext) ? $"{fname}" : $"{fname}.{_ext}";
      }
      set
      {
        fname = Path.GetFileNameWithoutExtension(value);
        ext = Path.GetExtension(value);
        if (isfile)
        {
          split[split.Count - 1] = fne;
        }
        else
        {
          split.Add(fne);
        }
      }
    }

    public string getAbsPath { get => uriAbs?.LocalPath.Replace("\\", "/"); }
    public string getRelType1 { get => uriRel?.ToString(); }
    public string getRelType2 { get => repRelSign2.Replace(uriRel?.ToString(), "."); }
    public string fname { get; internal set; } = "";
    string _ext;
    public string ext { set => _ext = value.TrimStart('.'); }

    public PathDecoderUri SetUriAbs(Uri uri)
    {
      uriAbs = uri;
      split = uriAbs.Segments.ToList();
      if (split?.First() == "/")
      {
        split.RemoveAt(0);
      }
      if (split?.Last().IndexOf('/') == -1)
      {
        fne = split.Last();
      }
      return this;
    }

    public PathDecoderUri(string curpath)
    {
      //this.curpath = curpath;
      curpath = curpath.TrimEnd('/') + "/";
      this.uriCurpath = new Uri(curpath, UriKind.Absolute);
      Clear();
    }

    public void Clear()
    {
      uriAbs = new Uri(uriCurpath.AbsolutePath.TrimEnd('/') + "/", UriKind.Absolute);
      uriRel = new Uri("./", UriKind.Relative);
      split?.Clear();
    }

    public static string ConvLegalRelPath(string relpath)
    {
      if (relpath?.Length > 0 && relpath[0] == '.')
      {
        while (repRelSign.IsMatch(relpath))
        {
          relpath = repRelSign.Replace(relpath, "../../");
        }
        return relpath;
      }
      return "";
    }

    public string AutoDecode(string absOrRel)
    {
      if (absOrRel?.Length > 2)
      {
        absOrRel = absOrRel.Replace("\\", "/").TrimStart('/');
        Uri tmpUriAbs;
        if (absOrRel[1] == ':')
        {
          tmpUriAbs = new Uri(absOrRel);
        }
        else if (absOrRel[0] == '.' || (absOrRel.IndexOfAny(new char[] { '/' }) != -1))
        {
          if (absOrRel[0] != '.')
          {
            absOrRel = "./" + absOrRel;
          }
          absOrRel = ConvLegalRelPath(absOrRel);
          uriRel = new Uri(absOrRel, UriKind.Relative);
          tmpUriAbs = new Uri(uriCurpath, uriRel);
        }
        else if (absOrRel.IndexOfAny(new char[] { '/', '\\', ':', '*', '?', '"', '<', '>', '|' }) == -1)
        {
          absOrRel = Path.Combine(uriCurpath.LocalPath, absOrRel);
          tmpUriAbs = new Uri(absOrRel);
        }
        else
        {
          Clear();
          return "";
        }
        SetUriAbs(tmpUriAbs);
        uriRel = uriCurpath.MakeRelativeUri(tmpUriAbs);
        return uriAbs.AbsolutePath;
      }
      Clear();
      return "";
    }

    public string RelToAbs(string rel)
    {
      try
      {
        rel = ConvLegalRelPath(rel);
        uriRel = new Uri(rel, UriKind.Relative);
        Uri tmpUriAbs = new Uri(uriCurpath, uriRel);
        SetUriAbs(tmpUriAbs);
        return tmpUriAbs.ToString();
      }
      catch (Exception)
      {
        return rel;
      }
    }

    public string AbsToRel(string abs)
    {
      try
      {
        Uri tmpUriAbs = new Uri(abs, UriKind.Absolute);
        SetUriAbs(tmpUriAbs);
        uriRel = uriCurpath.MakeRelativeUri(uriAbs);
        return uriRel.ToString();
      }
      catch (Exception)
      {
        return abs;
      }
    }
  }
}
